package com.ut.richpath;

import com.ut.richpath.model.Vector;
import com.ut.richpath.pathparser.PathParser;
import com.ut.richpath.util.JsonUtil;
import com.ut.richpath.util.LogUtil;
import com.ut.richpath.util.XmlParser;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.DragEvent;
import ohos.agp.components.Image;
import ohos.agp.render.BlendMode;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.utils.Color;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.Point;
import ohos.agp.utils.Rect;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

import java.util.ArrayList;
import java.util.List;

public class RichPathView extends Component implements Component.DrawTask, Component.EstimateSizeListener, RichPathDrawable.RefreshCallback {

    public final static String ATTRIBUTE_VECTOR = "rpv_vector";
    private Context context;
    private Vector vector;
    private RichPathDrawable richPathDrawable;
    private RichPath.OnPathClickListener onPathClickListener;
    private int resId = -1;

    public RichPathView(Context context) {
        super(context);
        init(context, null);
    }

    public RichPathView(Context context, AttrSet attrSet) {
        super(context, attrSet);
        init(context, attrSet);
    }

    private void init(Context context, AttrSet attrSet){
        this.context = context;
        vector = new Vector();
        if (attrSet==null)return;
        boolean present = attrSet.getAttr(ATTRIBUTE_VECTOR).isPresent();
        resId = present ? toIntegerValue(attrSet.getAttr(ATTRIBUTE_VECTOR).get().getStringValue()) : -1;
//        new Thread(()->setVectorDrawable(resId)).start();
        setTouchEventListener(touchEventListener);
        setEstimateSizeListener(this);
        addDrawTask(this);
        LogUtil.i("rich path view init complete");
    }

    private int toIntegerValue(String res){
        try {
            if (res==null)return -1;
            int index = res.indexOf(":")+1;
            String value = res.substring(index);
            return Integer.parseInt(value);
        } catch (NumberFormatException e) {
            return -1;
        }
    }

    public void setVectorDrawable(int resId){
        LogUtil.i("resId: %d", resId);

    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
//        if (resId==-1)return;
        try {
            //            context.getUITaskDispatcher().syncDispatch(()->setImageElement(richPathDrawable));
            richPathDrawable.drawToCanvas(canvas);

//            Matrix matrix = new Matrix();
//            matrix.postTranslate(30, 30);
//            matrix.postScale(15, 15, 30, 30);
//            float x=30,y=30;
//            float[] src = {x, y};
//            matrix.mapPoints(src);
//            LogUtil.i("map points: %s", JsonUtil.toJson(src));



//            Path path = new Path();
//            path.moveTo(20, 20);
//            path.rLineTo(60, 0);
//            path.rLineTo(0, 60);
//            path.rLineTo(-60, 0);
//            path.rLineTo(0, -60);

/*

            Path face = new Path();
            face.moveTo(16.6f, 3.8f);
            face.cubicTo(23.614016f, 3.8f, 29.3f, 9.485984f, 29.3f, 16.5f);
            face.cubicTo(29.3f, 23.514017f, 23.614016f, 29.2f, 16.6f, 29.2f);
            face.cubicTo(9.585983f, 29.2f, 3.9f, 23.514017f, 3.9f, 16.5f);
            face.cubicTo(3.9f, 9.485984f, 9.585983f, 3.8f, 16.6f, 3.8f);
            face.close();
            Path l_eye = new Path();
            float x = 13.0f, y = 7.2f;
            l_eye.moveTo(x, y);
            l_eye.cubicTo(new Point(x+2.3f, y+3.2f), new Point(x-4.0f, y+8.6f), new Point(x-4.0f, y+8.6f));
            x = x-4.0f;
            y = y+8.6f;
            l_eye.cubicTo(new Point(x, y), new Point(x-7.7f, y-2.1f), new Point(x-6.8f, y-6.1f));
            x = x-6.8f;
            y = y-6.1f;
            l_eye.cubicTo(new Point(x+0.9f, y-3.8f), new Point(x+5.4f, y-1.4f), new Point(x+5.4f, y-1.4f));
            x = x+5.4f;
            y = y-1.4f;
            l_eye.cubicTo(x, y,10.8f, 4.1f, 13.0f, 7.2f);
            l_eye.close();
            Path r_eye = new Path();
            x = 25.4f;
            y = 3.9f;
            r_eye.moveTo(x, y);
            r_eye.cubicTo(new Point(x+2.3f, y+3.2f), new Point(x-4.0f, y+8.6f), new Point(x-4.0f, y+8.6f));
            x = x-4.0f;
            y = y+8.6f;
            r_eye.cubicTo(new Point(x, y), new Point(x-7.7f, y-2.1f), new Point(x-6.8f, y-6.1f));
            x = x-6.8f;
            y = y-6.1f;
            r_eye.cubicTo(new Point(15.5f, 2.6f), new Point(20, 5), new Point(20, 5));
            x = 20f;
            y = 5f;
            r_eye.cubicTo(x, y,23.2f, 0.8f, 25.4f, 3.9f);
            r_eye.close();
            Path mouth = new Path();
            x = 18.8f;
            y = 27.4f;
            mouth.moveTo(x, y);
            mouth.cubicTo(new Point(x+3.9f, y-0.8f), new Point(x+6.5f, y-4.7f), new Point(x+5.6f, y-8.6f));
            mouth.rLineTo(-14.2f, 3.0f);
            mouth.cubicTo(new Point(11.1f, 25.7f), new Point(14.9f, 28.2f), new Point(18.8f, 27.4f));
            mouth.close();
            Path tongue = new Path();
            x = 12.8f;
            y = 25.9f;
            tongue.moveTo(x, y);
            tongue.cubicTo(new Point(x+1.6f, y+1.3f), new Point(x+3.8f, y+1.9f), new Point(x+6.0f, y+1.4f));
            x = x+6.0f;
            y = y+1.4f;
            tongue.cubicTo(new Point(x+2.2f, y-0.5f), new Point(x+4.0f, y-1.9f), new Point(x+5.0f, y-3.7f));
            x = x+5.0f;
            y = y-3.7f;
            tongue.cubicTo(new Point(x-2.1f, y-0.8f), new Point(x-3.6f, y-0.7f), new Point(x-5.8f, y-0.2f));
            x = x-5.8f;
            y = y-0.2f;
            tongue.cubicTo(15.7f, 23.9f,14.7f, 24.2f, 12.8f, 25.9f);
            tongue.close();

            face.transform(matrix);
            l_eye.transform(matrix);
            r_eye.transform(matrix);
            mouth.transform(matrix);
            tongue.transform(matrix);
            Paint paint = new Paint();
            paint.setColor(new Color(0xFFFFE254));
            paint.setStyle(Paint.Style.FILL_STYLE);
            paint.setStrokeWidth(2f);
            canvas.drawPath(face, paint);
            paint.setColor(new Color(0xFFF52C5B));
            paint.setStyle(Paint.Style.FILL_STYLE);
            canvas.drawPath(l_eye, paint);
            canvas.drawPath(r_eye, paint);
            paint.setColor(new Color(0xFFD61A4C));
            paint.setStyle(Paint.Style.FILL_STYLE);
            canvas.drawPath(mouth, paint);
            paint.setColor(new Color(0xFFF52C5B));
            paint.setStyle(Paint.Style.FILL_STYLE);
            canvas.drawPath(tongue, paint);
*/

/*

            float x = 19.5f, y = 6.2f;
            path.moveTo(x, y);
            path.rLineTo(1.3f, -1.3f);
            x = x+1.3f;
            y = y-1.3f;
            path.cubicTo(new Point(x+0.2f, y-0.2f), new Point(x+0.2f, y-0.5f), new Point(x+0.0f, y-0.7f));
            x = x;
            y = y-0.7f;
            path.cubicTo(new Point(x-0.2f, y-0.2f), new Point(x-0.5f, y-0.2f), new Point(x-0.7f, y+0.0f));
            x = x-0.7f;
            y = y+0.0f;
            path.rLineTo(-1.5f, 1.5f);
            x = x-1.5f;
            y = y+1.5f;
            path.cubicTo(17.9f, 5.2f, 16.9f, 5.0f, 16.0f, 5.0f);
            x = 16.0f;
            y = 5.0f;
            path.cubicTo(new Point(x-1.0f, y+0.0f), new Point(x-1.9f, y+0.2f), new Point(x-2.7f, y+0.6f));
            x = x-2.7f;
            y = y+0.6f;
            path.rLineTo(-1.5f, -1.5f);
            x = x-1.5f;
            y = y-1.5f;
            path.cubicTo(new Point(x-0.2f, y-0.2f), new Point(x-0.5f, y-0.2f), new Point(x-0.7f, y+0.0f));
            x = x-0.7f;
            y = y+0.0f;
            path.cubicTo(new Point(x-0.2f, y+0.2f), new Point(x-0.2f, y+0.5f), new Point(x+0.0f, y+0.7f));
            x = x+0.0f;
            y = y+0.7f;
            path.rLineTo(1.3f, 1.3f);
            x = x+1.3f;
            y = y+1.3f;
            path.cubicTo(11.0f, 7.3f, 10.0f, 9.0f, 10.0f, 11.0f);
            x = 10.0f;
            y = 11.0f;
            path.rLineTo(12.0f, 0f);
            x = x+12.0f;
            y = y+0.0f;
            path.cubicTo(22.0f, 9.0f, 21.0f, 7.2f, 19.5f, 6.2f);
            path.close();

            Path r_hand = new Path();
            x = 24.5f;
            y = 12.0f;
            r_hand.moveTo(x, y);
            r_hand.cubicTo(x-0.8f, y, x-1.5f, y+0.7f, x=x-1.5f, y=y+1.5f);
            r_hand.lineTo(x, y=y+7.0f);
            r_hand.cubicTo(x, y+0.8f, x+0.7f, y+1.5f, x=x+1.5f, y=y+1.5f);
            r_hand.cubicTo(x, y, x+1.5f, y-0.75f, x=x+1.5f, y=y-1.5f);
            r_hand.lineTo(x, y=y-7.0f);
            r_hand.cubicTo(26.0f, 12.7f, 25.3f, 12.0f, 24.5f, 12.0f);
            r_hand.close();

            path.transform(matrix);
            r_hand.transform(matrix);
            Paint paint = new Paint();
            paint.setColor(new Color(0xFFF52C5B));
            paint.setStyle(Paint.Style.STROKE_STYLE);
            paint.setStrokeWidth(2);
            canvas.drawPath(path, paint);
            canvas.drawPath(r_hand, paint);
*/


        } catch (Exception e) {
            e.printStackTrace();
            LogUtil.i("parse vector error msg: %s", e.getLocalizedMessage());
        }
    }

    @Override
    public boolean onEstimateSize(int widthEstimateConfig, int heightEstimateConfig) {
        try {
            XmlParser.parseVector(vector, resId, context);
//            LogUtil.i("rich path view vector: %s", JsonUtil.toJson(vector));
        } catch (Exception e) {
            LogUtil.i("rich path view error msg: %s", e.getLocalizedMessage());
            e.printStackTrace();
        }
        float desiredWidth = vector.getWidth();
        float desiredHeight = vector.getHeight();

        int widthMode = EstimateSpec.getMode(widthEstimateConfig);
        int widthSize = EstimateSpec.getSize(widthEstimateConfig);
        int heightMode = EstimateSpec.getMode(heightEstimateConfig);
        int heightSize = EstimateSpec.getSize(heightEstimateConfig);

        int width = (int) desiredWidth;
        if (widthMode==EstimateSpec.PRECISE){
            width = widthSize;
        }else if (widthMode==EstimateSpec.NOT_EXCEED){
            width = Math.min((int) desiredWidth, widthSize);
        }

        int height = (int) desiredHeight;
        if (heightMode==EstimateSpec.PRECISE){
            height = heightSize;
        }else if (heightMode==EstimateSpec.NOT_EXCEED){
            height = Math.min((int) desiredHeight, heightSize);
        }
        setEstimatedSize(
                Component.EstimateSpec.getChildSizeWithMode(width, width, Component.EstimateSpec.NOT_EXCEED),
                Component.EstimateSpec.getChildSizeWithMode(height, height, Component.EstimateSpec.NOT_EXCEED));
        LogUtil.i("width: %d, height: %d", width, height);
        richPathDrawable = new RichPathDrawable(context, resId, vector, Image.ScaleMode.INSIDE, this);
        richPathDrawable.onBoundsChange(new Rect(0, 0, width, height));

        return true;
    }

    public List<RichPath> findAllRichPaths(){
        return richPathDrawable!=null ? richPathDrawable.findAllRichPaths() : new ArrayList<>();
    }

    public RichPath findRichPathByName(String name){
        return richPathDrawable!=null ? richPathDrawable.findRichPathByName(name) : null;
    }

    /**
     * find the first [RichPath] or null if not found
     * <p>
     * This can be in handy if the vector consists of 1 path only
     *
     * @return the [RichPath] object found or null
     */
    public RichPath findFirstRichPath(){
        return richPathDrawable!=null ? richPathDrawable.findFirstRichPath() : null;
    }

    /**
     * find [RichPath] by its index or null if not found
     * <p>
     * Note that the provided index must be the flattened index of the path
     * <p>
     * example:
     * <pre>
     * {@code <vector>
     *     <path> // index = 0
     *     <path> // index = 1
     *     <group>
     *          <path> // index = 2
     *          <group>
     *              <path> // index = 3
     *          </group>
     *      </group>
     *      <path> // index = 4
     *   </vector>}
     * </pre>
     *
     * @param index the flattened index of the path
     * @return the [RichPath] object found or null
     */
    public RichPath findRichPathByIndex(int index){
        return richPathDrawable!=null ? richPathDrawable.findRichPathByIndex(index) : null;
    }

    public void addPath(String path) {
        if (richPathDrawable!=null){
            richPathDrawable.addPath(PathParser.createPathFromPathData(path));
        }
    }

    public void addPath(Path path){
        if (richPathDrawable!=null){
            richPathDrawable.addPath(path);
        }
    }

    private TouchEventListener touchEventListener = new TouchEventListener() {

        @Override
        public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
            if (touchEvent.getAction()==TouchEvent.PRIMARY_POINT_UP){
                touchEvent.getPointerPosition(0).getX();
                callOnClick();
            }
            if (richPathDrawable!=null){
                RichPath richPath = richPathDrawable.getTouchedPath(touchEvent);
                if (richPath!=null&&richPath.getOnPathClickListener()!=null){
                    richPath.getOnPathClickListener().onClick(richPath);
                }
                if (onPathClickListener!=null&&richPath!=null){
                    onPathClickListener.onClick(richPath);
                }
            }

            return true;
        }
    };

    public RichPath.OnPathClickListener getOnPathClickListener() {
        return onPathClickListener;
    }

    public void setOnPathClickListener(RichPath.OnPathClickListener onPathClickListener) {
        this.onPathClickListener = onPathClickListener;
    }

    //    override fun onTouchEvent(event: MotionEvent?): Boolean {
//        when(event?.action) {
//            MotionEvent.ACTION_UP -> {
//                performClick()
//            }
//        }
//
//        richPathDrawable?.getTouchedPath(event)?.let { richPath ->
//                richPath.onPathClickListener?.onClick(richPath)
//            this.onPathClickListener?.onClick(richPath)
//        }
//        return true
//    }

    @Override
    public void refresh() {
        invalidate();
    }
}
